#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

#include <stdarg.h>

static char digits[] = "0123456789ABCDEF";

static void
putc(int fd, char c)
{
    write(fd, &c, 1);
}

static void
printint(int fd, int xx, int base, int sgn)
{
    char buf[16];
    int i, neg;
    uint x;

    neg = 0;
    if (sgn && xx < 0)
    {
        neg = 1;
        x = -xx;
    }
    else
    {
        x = xx;
    }

    i = 0;
    do
    {
        buf[i++] = digits[x % base];
    } while ((x /= base) != 0);
    if (neg)
        buf[i++] = '-';

    while (--i >= 0)
        putc(fd, buf[i]);
}

static void
printptr(int fd, uint64 x)
{
    int i;
    putc(fd, '0');
    putc(fd, 'x');
    for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
        putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
}

// Print to the given fd. Only understands %d, %x, %p, %s.
void vprintf(int fd, const char *fmt, va_list ap)
{
    char *s;
    int c, i, state;

    state = 0;
    for (i = 0; fmt[i]; i++)
    {
        c = fmt[i] & 0xff;
        if (state == 0)
        {
            if (c == '%')
            {
                state = '%';
            }
            else
            {
                putc(fd, c);
            }
        }
        else if (state == '%')
        {
            if (c == 'd')
            {
                printint(fd, va_arg(ap, int), 10, 1);
            }
            else if (c == 'l')
            {
                printint(fd, va_arg(ap, uint64), 10, 0);
            }
            else if (c == 'x')
            {
                printint(fd, va_arg(ap, int), 16, 0);
            }
            else if (c == 'p')
            {
                printptr(fd, va_arg(ap, uint64));
            }
            else if (c == 's')
            {
                s = va_arg(ap, char *);
                if (s == 0)
                    s = "(null)";
                while (*s != 0)
                {
                    putc(fd, *s);
                    s++;
                }
            }
            else if (c == 'c')
            {
                putc(fd, va_arg(ap, uint));
            }
            else if (c == '%')
            {
                putc(fd, c);
            }
            else
            {
                // Unknown % sequence.  Print it to draw attention.
                putc(fd, '%');
                putc(fd, c);
            }
            state = 0;
        }
    }
}

void fprintf(int fd, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    vprintf(fd, fmt, ap);
}

void printf(const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    vprintf(1, fmt, ap);
}
